package lib.random;

import java.util.ArrayList;

/**
 * Generates random 2D maps.
 * 
 * @author dolphinigle
 */
public class RandomMap {

  /**
   * Random source
   */
  protected ExtendedRandom random_;

  /**
   * Constructs a random graph generator
   * @param random base random seed source
   */
  public RandomMap(ExtendedRandom random) {
    random_ = random;
  }

  /**
   * Randomly replaces some character(s) in field with newchar with probability
   * equal to fillchance per such char
   * @param field
   * @param newchar
   * @param fillchance
   * @return
   */
  public String[] randomFill(String[] field, char newchar, double fillchance) {
    String[] ret = new String[field.length];

    for (int i = 0; i < field.length; ++i) {
      StringBuilder builder = new StringBuilder();
      for (int j = 0; j < field[i].length(); ++j) {
        if (random_.nextDouble() < fillchance) {
          builder.append(newchar);
        } else {
          builder.append(field[i].charAt(j));
        }
      }
      ret[i] = builder.toString();
    }

    return ret;
  }

  /**
   * Fills a random shortest path in the grid.
   * <br/>
   * <br/>
   * More specifically, fills one random path from (row1, col1) to (row2, col2)
   * with new_char.
   * @param field the grid
   * @param row1 start row
   * @param col1 start col
   * @param row2 end row
   * @param col2 end col
   * @param new_char character to fill the path with
   * @return grid with the path filled
   */
  public String[] randomShortestPathFill(String[] field, int row1, int col1,
          int row2, int col2, char new_char) {

    int steps_required = Math.abs(row2 - row1) + Math.abs(col2 - col1);
    int cols_required = Math.abs(col2 - col1);

    int[] when_to_move_col =
            random_.nextCombination(steps_required, cols_required);

    char[][] new_fill = new char[field.length][field[0].length()];
    for (int r = 0; r < field.length; ++r) {
      for (int c = 0; c < field[0].length(); ++c) {
        new_fill[r][c] = field[r].charAt(c);
      }
    }

    int cur_row = row1;
    int cur_col = col1;

    int wtmc_it = 0;

    for (int i = 0; i < steps_required; ++i) {
      new_fill[cur_row][cur_col] = new_char;
      if (wtmc_it < when_to_move_col.length &&
              when_to_move_col[wtmc_it] == i) {
        ++wtmc_it;
        cur_col = Integer.signum(col2 - col1) + cur_col;
      } else {
        cur_row = Integer.signum(row2 - row1) + cur_row;
      }
      new_fill[cur_row][cur_col] = new_char;
    }

    String[] ret = new String[field.length];
    for (int r = 0; r < field.length; ++r) {
      StringBuilder builder = new StringBuilder();
      for (int c = 0; c < field[0].length(); ++c) {
        builder.append(new_fill[r][c]);
      }
      ret[r] = builder.toString();
    }

    return ret;

  }

  /**
   * Fills a random shortest path in the grid but does not repaint the start
   * location
   * <br/>
   * <br/>
   * More specifically, fills one random path from (row1, col1) to (row2, col2)
   * with new_char.
   * @param field the grid
   * @param row1 start row
   * @param col1 start col
   * @param row2 end row
   * @param col2 end col
   * @param new_char character to fill the path with
   * @return grid with the path filled
   */
  public String[] randomShortestPathFillIgnoreStart(String[] field, int row1, int col1,
          int row2, int col2, char new_char) {

    int steps_required = Math.abs(row2 - row1) + Math.abs(col2 - col1);
    int cols_required = Math.abs(col2 - col1);

    int[] when_to_move_col =
            random_.nextCombination(steps_required, cols_required);

    char[][] new_fill = new char[field.length][field[0].length()];
    for (int r = 0; r < field.length; ++r) {
      for (int c = 0; c < field[0].length(); ++c) {
        new_fill[r][c] = field[r].charAt(c);
      }
    }

    int cur_row = row1;
    int cur_col = col1;

    int wtmc_it = 0;

    for (int i = 0; i < steps_required; ++i) {
      if (wtmc_it < when_to_move_col.length &&
              when_to_move_col[wtmc_it] == i) {
        ++wtmc_it;
        cur_col = Integer.signum(col2 - col1) + cur_col;
      } else {
        cur_row = Integer.signum(row2 - row1) + cur_row;
      }
      new_fill[cur_row][cur_col] = new_char;
    }

    String[] ret = new String[field.length];
    for (int r = 0; r < field.length; ++r) {
      StringBuilder builder = new StringBuilder();
      for (int c = 0; c < field[0].length(); ++c) {
        builder.append(new_fill[r][c]);
      }
      ret[r] = builder.toString();
    }

    return ret;

  }

  /**
   * Fills a grid with some exact number of occurences of a character
   * @param field initial grid. Cells marked with '$' will not be replaced
   * @param possible_chars list of possible replacements with equal probability
   * @param occurence_count number of times a cell have to be replaced
   * @return a grid with occurence_count cells replaced
   */
  public String[] randomFillX(String[] field, String possible_chars, int occurence_count) {

    ArrayList<Integer> rs = new ArrayList<Integer>();
    ArrayList<Integer> cs = new ArrayList<Integer>();

    int row = field.length;
    int col = field[0].length();

    for (int r = 0; r < row; ++r) for (int c = 0; c < col; ++c) {
      if (field[r].charAt(c) == '$') continue;
      rs.add(r);
      cs.add(c);
    }

    assert (rs.size() >= occurence_count);
    
    char[][] buffer = new char[row][col];
    for (int r = 0; r < row; ++r) for (int c = 0; c < col; ++c) {
      buffer[r][c] = field[r].charAt(c);
    }

    int[] replaced = random_.nextCombination(rs.size(), occurence_count);
    for (int i = 0; i < replaced.length; ++i) {
      char rchar = possible_chars.charAt(
              random_.nextInt(possible_chars.length()));
      buffer[rs.get(replaced[i])][cs.get(replaced[i])] = rchar;
    }

    String[] ret = new String[row];
    for (int r = 0; r < row; ++r) {
      StringBuilder builder = new StringBuilder();
      for (int c = 0; c < col; ++c) {
        builder.append(buffer[r][c]);
      }
      ret[r] = builder.toString();
    }

    return ret;

  }

  /**
   * Randomly fill a submatrix from field
   * @param field the grid
   * @param possible_chars possible chars to fill the submtraix
   * @param topr top r of the submatrix
   * @param topc top c of the submatrix
   * @param lenr height of the submatrix
   * @param lenc width of the submatrix
   * @return field with the submatrix refilled
   */
  public String[] randomFillSubMatrix(String[] field, String possible_chars,
          int topr, int topc, int lenr, int lenc) {

    int row = field.length;
    int col = field[0].length();

    char[][] buffer = new char[row][col];
    for (int r = 0; r < row; ++r) for (int c = 0; c < col; ++c) {
      buffer[r][c] = field[r].charAt(c);
      if (r >= topr && r < topr + lenr && c >= topc && c < topc + lenc) {
        buffer[r][c] = possible_chars.charAt(
                random_.nextInt(possible_chars.length()));
      }
    }

    String[] ret = new String[row];
    for (int r = 0; r < row; ++r) {
      StringBuilder builder = new StringBuilder();
      for (int c = 0; c < col; ++c) {
        builder.append(buffer[r][c]);
      }
      ret[r] = builder.toString();
    }

    return ret;

  }

}
